//页面性能监测
var stats = new Stats(),
    sds = stats.domElement.style;
sds.position = 'absolute';
sds.top = '0px';
sds.right = '0px';
document.body.appendChild(stats.domElement);

//烟花
var Fireworks = function() {
    var self = this;
    
    /**
     * 在一定范围内返回一个随机值
     * @param {Number} rMi 范围最小值
     * @param {Number} rMa 范围最大值
     */
    function rand(rMi, rMa) {
        return ~~((Math.random() * (rMa - rMi + 1)) + rMi);
    }

    /**
     * 判断两个矩形是否碰撞
     * @param {Number} x1 矩形1的起始坐标点x值
     * @param {Number} y1 矩形1的起始坐标点y值
     * @param {Number} w1 矩形1的宽
     * @param {Number} h1 矩形1的高
     * @param {Number} x2 矩形2的起始坐标点x值
     * @param {Number} y2 矩形2的起始坐标点y值
     * @param {Number} w2 矩形2的宽
     * @param {Number} h2 矩形2的高
     */
    function hitTest(x1, y1, w1, h1, x2, y2, w2, h2) {
        return !(x1 + w1 < x2 + w2 || x2 + w2 < x1 || y1 + h1 < y2 || y2 + h2 < y1);
    }
    
    /*=============================================================================*/
    /* 初始化
    /*=============================================================================*/
    self.init = function() {
        self.dt = 0;
        self.oldTime = Date.now();
        self.canvas = document.getElementById('c');
        self.canvas.width = self.cw = 600;
        self.canvas.height = self.ch = 400;
        self.ctx = self.canvas.getContext('2d');
        self.ctx.lineCap = self.ctx.lineJoin = 'round';

        self.lineWidth = 1;
        self.particles = []; //粒子数组
        self.fireworks = []; //烟花对象数组
        self.mx = self.cw / 2; //中点横坐标
        self.my = self.ch / 2; //中点纵坐标
        self.currentHue = 170; //色轮度数
        self.partCount = 30; //爆炸后形成粒子的个数
        self.partSpeed = 5; //爆炸后形成粒子的移动速度
        self.partSpeedVariance = 10; //爆炸后形成粒子速度取值范围的变化量
        self.partWind = 50; //爆炸后形成粒子的偏转量
        self.partFriction = 5; //爆炸后形成粒子炸开的距离
        self.partGravity = 1; //爆炸后形成粒子受到的重力
        self.flickerDensity = 20; //爆炸后形成粒子闪烁的密度
        self.hueMin = 150; //色轮取值范围的最小值
        self.hueMax = 200; //色轮取值范围的最大值
        self.fworkSpeed = 2; //烟火的移动速度
        self.fworkAccel = 4; //烟火的加速度
        self.hueVariance = 30; //色轮的变化量
        self.showShockwave = false; //是否显示震动波浪
        self.showTarget = true; //是否显示爆炸点标志
        self.clearAlpha = 25; //叠加层透明值

        self.handleEvent();
        self.animate();
    }

    /*=============================================================================*/
    /* 粒子类
    /*=============================================================================*/
    var Particle = function(x, y, hue) {
        this.x = x || 0; //横坐标
        this.y = y || 0; //纵坐标
        this.coordLast = [
            {x: x, y: y},
            {x: x, y: y},
            {x: x, y: y}
        ];
        this.angle = rand(0, 360); //运动方向
        this.speed = rand(((self.partSpeed - self.partSpeedVariance) <= 0) ? 1 : self.partSpeed - self.partSpeedVariance, self.partSpeed + self.partSpeedVariance)
        this.friction = 1 - self.partFriction / 100;
        this.gravity = self.partGravity / 2; //加速度
        this.hue = rand(hue - self.hueVariance, hue + self.hueVariance);
        this.brightness = rand(50, 80); //亮度
        this.alpha = rand(40, 100) / 100; //透明值
        this.decay = rand(10, 50) / 1000; //透明度衰减值
        this.wind = (rand(0, self.partWind) - (self.partWind / 2)) / 25; //末尾卷曲系数
        this.lineWidth = self.lineWidth;
    }
    //粒子类原型
    Particle.prototype.update = function(index) {
        var radians = this.angle * Math.PI / 180; //运动方向弧度化
        var vx = Math.cos(radians) * this.speed; //水平方向速度
        var vy = Math.sin(radians) * this.speed + this.gravity; //竖直方向速度
        this.speed *= this.friction;

        //当前位置坐标进栈
        this.coordLast[2].x = this.coordLast[1].x;
        this.coordLast[2].y = this.coordLast[1].y;
        this.coordLast[1].x = this.coordLast[0].x;
        this.coordLast[1].y = this.coordLast[0].y;
        this.coordLast[0].x = this.x;
        this.coordLast[0].y = this.y;

        this.x += vx * self.dt;
        this.y += vy * self.dt;

        this.angle += this.wind;
        this.alpha -= this.decay;

        //判断是否从数组中删除粒子
        if(!hitTest(0, 0, self.cw, self.ch, this.x - this.lineWidth, this.y - this.lineWidth, this.lineWidth * 2, this.lineWidth * 2) || this.alpha < .05) {
            self.particles.splice(index, 1);
        }
    }
    Particle.prototype.draw = function() {
        var coordRand = (rand(1, 3) - 1);
        self.ctx.save();
        self.ctx.beginPath();
        self.ctx.moveTo(Math.round(this.coordLast[coordRand].x), Math.round(this.coordLast[coordRand].y));
        self.ctx.lineTo(Math.round(this.x), Math.round(this.y));
        self.ctx.closePath();
        self.strokeStyle = `hsla(${this.hue}, 100%, ${this.brightness}%, ${this.alpha})`;
        self.ctx.stroke();

        //控制闪烁密度
        if(self.flickerDensity > 0) {
            var flickerDensity = 50 - self.flickerDensity;
            if(rand(0, flickerDensity) === flickerDensity) {
                self.ctx.beginPath();
                self.ctx.arc(Math.round(this.x), Math.round(this.y), rand(this.lineWidth, this.lineWidth+3) / 2, 0, Math.PI * 2, false);
                self.ctx.closePath();
                var randAlpah = rand(50, 100) / 100;
                self.ctx.fillStyle = `hsla(${this.hue}, 100%, ${this.brightness}%, ${randAlpah})`;
                self.ctx.fill();
            }
        }
    }

    /*=============================================================================*/
    /* 粒子对象操作
    /*=============================================================================*/
    self.createParticles = function(x, y, hue) {
        var countdown = self.partCount;
        while(countdown--) {
            self.particles.push(new Particle(x, y, hue));
        }
    }
    self.updateParticles = function() {
        var i = self.particles.length - 1;
        while(i >= 0) {
            var p = self.particles[i];
            p.update(i);
            i -= 1;
        }
    }
    self.drawParticles = function() {
        var i = self.particles.length - 1;
        while(i >= 0) {
            var p = self.particles[i];
            p.draw();
            i -= 1;
        }
    }

    /*=============================================================================*/
    /* 烟花类
    /*=============================================================================*/
    var Firework = function(startX, startY, targetX, targetY) {
        this.x = startX;
        this.y = startY;
        this.startX = startX;
        this.startY = startY;
        this.hitX = false;
        this.hitY = false;
        this.coordLast = [
            {x: startX, y: startY},
            {x: startX, y: startY},
            {x: startX, y: startY}
        ];
        this.targetX = targetX;
        this.targetY = targetY;
        this.speed = self.fworkSpeed;
        this.angle = Math.atan2(targetY - startY, targetX - startX);
        this.showShockwave = Math.atan2(targetY - startY, targetX - startX) + (90 * Math.PI / 180);
        this.acceleration = self.fworkAccel / 100;
        this.hue = self.currentHue;
        this.brightness = rand(50, 80),
        this.alpha = rand(50, 100) / 100;
        this.lineWidth = self.lineWidth;
        this.targetRadius = 1;
    }
    //烟花类原型
    Firework.prototype.update = function(index) {
        self.ctx.lineWidth = this.lineWidth;
        var vx = Math.cos(this.angle) * this.speed;
        var vy = Math.sin(this.angle) * this.speed;
        this.speed *= 1 + this.acceleration;

        this.coordLast[2].x = this.coordLast[1].x;
		this.coordLast[2].y = this.coordLast[1].y;
		this.coordLast[1].x = this.coordLast[0].x;
		this.coordLast[1].y = this.coordLast[0].y;
		this.coordLast[0].x = this.x;
        this.coordLast[0].y = this.y;
        
        if(self.showTarget) { //若要展示目标点
            if(this.targetRadius < 8) {
                this.targetRadius += .25 * self.dt;
            } else {
                this.targetRadius = 1 * self.dt;
            }
        }

        //判断烟花点x坐标是否已到爆炸点
        if(this.startX >= this.targetX) {
            if(this.x + vx <= this.targetX) {
                this.x = this.targetX;
                this.hitX = true;
            } else {
                this.x += vx * self.dt;
            }
        } else {
            if(this.x + vx >= this.targetX) {
                this.x = this.targetX;
                this.hitX = true;
            } else {
                this.x += vx * self.dt;
            }
        }

        //判断烟花点y坐标是否已到爆炸点
        if(this.startY >= this.targetY) {
            if(this.y + vy <= this.targetY) {
                this.y = this.targetY;
                this.hitY = true;
            } else {
                this.y += vy * self.dt;
            }
        } else {
            if(this.y + vy >= this.targetY) {
                this.y = this.targetY;
                this.hitY = true;
            } else {
                this.y += vy * self.dt;
            }
        }

        //判断是否爆炸
        if(this.hitX && this.hitY) {
            var randExplosion = rand(0, 9);
            self.createParticles(this.targetX, this.targetY, this.hue);
            self.fireworks.splice(index, 1);
        }
    }
    Firework.prototype.draw = function() {
        self.ctx.lineWidth = this.lineWidth;
        var coordRand = (rand(1, 3)  - 1);
        self.ctx.beginPath();							
		self.ctx.moveTo(Math.round(this.coordLast[coordRand].x), Math.round(this.coordLast[coordRand].y));
		self.ctx.lineTo(Math.round(this.x), Math.round(this.y));
		self.ctx.closePath();
		self.ctx.strokeStyle = 'hsla('+this.hue+', 100%, '+this.brightness+'%, '+this.alpha+')';
        self.ctx.stroke();
        
        if(self.showTarget){
			self.ctx.save();
			self.ctx.beginPath();
			self.ctx.arc(Math.round(this.targetX), Math.round(this.targetY), this.targetRadius, 0, Math.PI*2, false)
			self.ctx.closePath();
			self.ctx.lineWidth = 1;
			self.ctx.stroke();
			self.ctx.restore();
        }
    }
    

    /*=============================================================================*/
    /* 烟花对象操作
    /*=============================================================================*/
    self.createFireworks = function(startX, startY, targetX, targetY) {
        self.fireworks.push(new Firework(startX, startY, targetX, targetY));
    }
    self.updateFireworks = function() {
        var i = self.fireworks.length - 1;
        while(i >= 0) {
            self.fireworks[i].update(i);
            i--;
        }
    }
    self.drawFireworks = function() {
        var i = self.fireworks.length - 1;
        while(i >= 0) {
            self.fireworks[i].draw(i);
            i--;
        }
    }

    
    /*=============================================================================*/
    /* 更新变化量
    /*=============================================================================*/
    self.updateDelta = function() {
        var newTime = Date.now();
        self.dt = (newTime - self.oldTime) / 16;
        self.dt = (self.dt > 5) ? 5 : self.dt;
        self.oldTime = newTime;
    }


    /*=============================================================================*/
    /* 事件处理
    /*=============================================================================*/
    self.handleEvent = function() {
        //mousedown事件处理函数
        function handleMousedown(e) {
            var event = e || window.event;
            var cBound = self.canvas.getBoundingClientRect();
            self.mx = event.pageX - cBound.left;
            self.my = event.pageY - cBound.top;
            self.currentHue = rand(self.hueMin, self.hueMax);
            self.createFireworks(self.cw / 2, self.ch, self.mx, self.my);
            //mousemove事件
            self.canvas.addEventListener('mousemove', handleMousemove, false);
            //mouseup事件
            self.canvas.addEventListener('mouseup', handleMouseup, false);
        }

        //mousemove事件处理函数
        function handleMousemove(e) {
            var event = e || window.event;
            var cBound = self.canvas.getBoundingClientRect();
            self.mx = event.pageX - cBound.left;
            self.my = event.pageY - cBound.top;
            self.currentHue = rand(self.hueMin, self.hueMax);
            self.createFireworks(self.cw / 2, self.ch, self.mx, self.my);
        }

        //mouseup事件处理函数
        function handleMouseup(e) {
            self.canvas.removeEventListener('mousemove', handleMousemove);
            self.canvas.removeEventListener('mouseup', handleMouseup);
        }

        //mousedown事件
        self.canvas.addEventListener('mousedown', handleMousedown, false);
    }

    /*=============================================================================*/
    /* 动画函数
    /*=============================================================================*/
    self.animate = function() {
        // self.ctx.clearRect(0, 0, self.cw, self.ch);
        requestAnimationFrame(self.animate);
        self.updateDelta();
        self.ctx.fillStyle = 'rgba(0, 0, 0, 1)';
        self.ctx.fillRect(0, 0, self.cw, self.ch);
		self.ctx.globalCompositeOperation = 'destination-out';
        self.ctx.fillStyle = 'rgba(0, 0, 0, '+ self.clearAlpha / 100 +')';
        self.ctx.fillRect(0, 0, self.cw, self.ch);
		self.ctx.globalCompositeOperation = 'lighter';
		self.updateFireworks();
		self.updateParticles();
		self.drawFireworks();			
		self.drawParticles();		

        stats.update();
    }

    self.init();
}

var fworks = new Fireworks();